#include <cstdio>
#include "server.hpp"
/**
	Constructeur d'un objet Serveur
*/
Server::Server(int port, FString logDir)
{
	nbClients = 0;
	lport = port;
	activate = false;

	for(int i=0; i<CMAX; i++)
		clients[i] = 0;
	// set the address structure
	serverAddress.sin_family = AF_INET;
	serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
	serverAddress.sin_port = htons(lport);
	FString logFile = logDir;
	logFile += "/msg.log";
	FString logFile2 = logDir;
	logFile2 += "/server.log";
	logMsg = new Log(logFile);
	logSvr = new Log(logFile2);
}

/**
	Destructeur de l'objet Serveur
*/
Server::~Server()
{

}

/**
	Methode permettant de demarrer le serveur
*/
void Server::start()
{
	activate = true;
 	// Creation du socket server
//	sockServer = socket(AF_INET, SOCK_STREAM, 0);
	sockServer = socket(AF_INET, SOCK_STREAM, 0);

	int somerandomint = 1;
	setsockopt(sockServer, SOL_SOCKET, SO_REUSEADDR, &somerandomint, sizeof(int));

	if(sockServer > 0)
	{
		// bind du socket
		int bResult = bind(sockServer,
								 (struct sockaddr *) &serverAddress,
								 sizeof(serverAddress));

		if(bResult < 0)
		{
			cout << "\t\t\tFail !" << endl;
			cout << "Connexion error" << endl;
			close(sockServer);
		}
		else
		{
			listen(sockServer, CMAX);
			cout << "Starting server..." << endl;
			cout << ":: Server is listening on port " << lport << endl;
			FString log = "Server started on port ";
			log += FString::fromInt(lport);
			logSvr->logger(log);
			highestSock = sockServer;
			runLoop();
		}
	}
	else
	{
		cout << "\t\t\tFail !" << endl;
		cout << "can't create socket" << endl;
	}
}

/** 
	 Methode permettant d'arreter proprement le serveur
*/
void Server::stop()
{
	activate = false;
	cout << "Stopping server...\t\t\tOK" << endl;
	FString log = "Server stopped";
	logSvr->logger(log);
}

/**
	Crée la liste des descripteurs à écouter
*/
void Server::initList()
{
	/* on commence par ajouter l'entrée standard */
	FD_ZERO(&socksToListen);
	FD_SET(sockServer, &socksToListen);
	FD_SET(0, &socksToListen);

	/* puis on ajoute chaque socket de client */
	for(int i=0; i<CMAX; i++)
	{
		if(clients[i] != 0)
		{
			FD_SET(clients[i]->getSock(), &socksToListen);
			if(clients[i]->getSock() > highestSock)
				highestSock = clients[i]->getSock();
		}
	}
}

/**
	Lis les différents descripteurs de fichiers
	afin d'y récuprérer les informations présentes
*/
void Server::readSocks()
{
	/* tentative de connexion */
	if(FD_ISSET(sockServer, &socksToListen))
	{
		cout << "\n::Un nouveau client essaye de se connecter...";
		int connect = accept(sockServer, NULL, NULL);
		if(connect < 0)
		{
			cout << "\t\tEchoue!" << endl;
		}
		else
		{
			for(int i=0; i<CMAX; i++)
			{
				if(clients[i] == 0)
				{
					clients[i] = new User(connect);
					nbClients++;

					FString str("YID ");
					str += FString::fromInt(connect);
					sendMsg(clients[i], str);
					
					cout << "\t\tOK" << endl;
					cout << "  -> assigne a " << i << endl;

					FString log = "New client connected on slot";
					log += FString::fromInt(i);
					logSvr->logger(log);
					
					showPrompt();
					return;
				}
			}
			/* si on arrive ici, c'est qu'il n'y a plus de place */
			close(connect);
			cout << "\t\t\tEchoue!" << endl;
		}
	}

	/* evenements sur stdin */
	if(FD_ISSET(0, &socksToListen))
	{
		if(read(0, buff, BUFFSIZE))
		{
			// on enlève le '\n' à la fin
			for(int i=0; i<BUFFSIZE; i++)
			{
				if(buff[i] == '\n')
				{
					buff[i] = '\0';
					i = BUFFSIZE; // Bourrin : pour sortir :)
				}
			}
			if(strcmp("",buff) != 0)
			{
				processCmd(buff);
			}
		}
	}

	/* autres evenements */
	for(int i=0; i<CMAX; i++)
	{
		if(clients[i] && FD_ISSET(clients[i]->getSock(), &socksToListen))
		{
			int rec_res = recv(clients[i]->getSock(), buff, BUFFSIZE, 0);
			/* un client se deconnecte */
			if(rec_res == 0)
			{
				FString log = "Client ";
				log += clients[i]->getName();
				log += " has left";
				logSvr->logger(log);

				cout << "\n::Client n°" << i << " a quitte" << endl;
				FString str("LEAVE ");
				str += FString::fromInt(clients[i]->getSock());
				close(clients[i]->getSock());
				delete clients[i];
				clients[i] = 0;

				// avoid hole in tab
				for(int k=i; k<nbClients; k++)
					clients[k] = clients[k+1];
				
				nbClients--;

				forward(str);
			}
			else if(rec_res > 0)
			{
				cout << "\n::Reçu: " << buff << endl;
				cout << "  du client n°" << i << endl;
				FString log = "From slot";
				log += FString::fromInt(i)+": "+buff;
				logSvr->logger(log);
				handleRequest(clients[i], buff);
			}
		}
	}

	showPrompt();
}

/** 
	 Methode permettant d'envoyer un message à tous les clients inscrits
	 sauf à celui qui a envoyé le message
*/
void Server::forwardFrom(int from, FString str)
{
	for(int i=0; i<nbClients; i++)
	{
		if(clients[i] && from != clients[i]->getSock())
			sendMsg(clients[i], str);
	}
}

/**
	Envoyer un message à tous les clients
*/
void Server::forward(FString str)
{
	for(int i=0; i<nbClients; i++)
	{
		if(clients[i])
		{
			sendMsg(clients[i], str);
		}
	}
}

void Server::sendMsg(User *cli, FString msg)
{
	msg += EOF;
	send(cli->getSock(), msg.data(), msg.size(), 0);
}

/**
	Methode permettant de traiter une information envoyée par un client
*/
void Server::handleRequest(User *cli, char *request)
{

	FString req(request);
	FString cmd = req.getCmd();
	FString arg = req.getArgs();

	/* traitement de la requete INFO */
	if(cmd.compare("LIST") == 0)
	{
		FString str("LIST ");
			
		for(int i=0; i<nbClients; i++)
		{
			str += FString::fromInt(clients[i]->getSock()) + " ";
			str += clients[i]->getName()+" ; ";
		
		}
		sendMsg(cli, str);		
	}

	/* traitement de la requete MSG */
	else if(cmd.compare("MSG") == 0)
	{
		FString str;
		str += cmd + " " + FString::fromInt(cli->getSock()) + " " + arg;
		forwardFrom(cli->getSock(), str);

		FString log = "From ";
		log += cli->getName() + " : " + arg ;
		logMsg->logger(log);
	}

	/* privates msg */
	else if(cmd.compare("PMSG") == 0)
	{
		FString msg = arg;
		int idUser = msg.getCmd().toInt();
		cout << "--- " << msg << endl;
		
		idUser = checkSocket(idUser);
		if(idUser > 0)
		{
			FString str("PMSG ");
			str += FString::fromInt(cli->getSock())+" "+msg.getArgs();
			User *to = findClient(idUser);
			if(to)
				sendMsg(to, str);
			
		}
		
	}

	/* traitement de la requete DETAILS */
	else if(cmd.compare("NICK") == 0)
	{
		FString nick = arg.getCmd();
		
		if(nick.compare("") != 0)
		{
			cli->setName(nick);
			if(cli->isRegistered())
			{
				FString str("NICK ");
				str += FString::fromInt(cli->getSock()) + " " + nick;
				forward(str);
			}
			else
			{
				FString str("NC ");
				str += nick;
				forward(str);
				cli->registerMe();
			}
		}
	}
}

/** 
	 Boucle d'exécution du serveur
	 on y écoute les différents sockets
*/
void Server::runLoop()
{
	int nbSocksToRead;
	struct timeval timeout;
	showPrompt();
	while(activate)
	{

		for(int k=0; k<BUFFSIZE; k++) //Initialisation du buffer
			buff[k] = '\0';
		initList();

		timeout.tv_sec = 1;
		timeout.tv_usec = 0;
		nbSocksToRead = select(highestSock+1, &socksToListen, NULL, NULL, &timeout);
		if(nbSocksToRead > 0)
		{
			readSocks();
		}
		alertClients();
	}
}

/**
	Check if a socket is registered in client lists
	returns socket descriptor if exists, -1 if it doesn't
*/
int Server::checkSocket(int id)
{
	for(int i=0; i<nbClients;  i++)
	{
		if(clients[i]->getSock() == id)
			return id;
	}
	return -1;
}

User* Server::findClient(int id)
{
	for(int i=0; i<nbClients;  i++)
	{
		if(clients[i]->getSock() == id)
			return clients[i];
	}	
	return 0;
}


/** 
	 Methode qui permet d'alerter les clients que une enchere est terminée
*/
void Server::alertClients()
{
	
}


